# -*- coding: utf-8 -*-

###############################################################################
#    KShootout - online shootout card game.                                   #
#    Copyright (C) 2008, 2009  MacJariel                                      #
#                                                                             #
#    KShootout is free software: you can redistribute it and/or modify        #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation, either version 3 of the License, or        #
#    (at your option) any later version.                                      #
#                                                                             #
#    KShootout is distributed in the hope that it will be useful,             #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
#    GNU General Public License for more details.                             #
#                                                                             #
#    You should have received a copy of the GNU General Public License        #
#    along with KShootout.  If not, see <http://www.gnu.org/licenses/>.       #
###############################################################################

from UserDict import *
from textwrap import *

constrWrapper = TextWrapper(width = 70, subsequent_indent = "         ")


def genDoxyString(doxyString, author):
    lines = []
    for line in doxyString.splitlines():
        lines.extend(wrap(line, 70))
    lines.append("@author: " + author)
    return ("/**\n" + "".join([" * " + line + "\n" for line in lines]) + " */")

class Member(UserDict):
    def __init__(self, type, name, elemName, doxyString, initVal = None, flagged = False):
        self.data = {}
        self["name"] = name
        self["type"] = type
        self["elemName"] = "\"" + elemName + "\""
        self["doxyString"] = doxyString
        self["initVal"] = initVal
        self["flagged"] = flagged
        if (flagged):
            self["flagName"] = name[:1].upper() + name[1:] + "Attr"

class Struct(UserDict):
    def __init__(self, structName, elemName):
        self.data = {}
        self["structName"] = structName
        self["elemName"] = "\"" + elemName + "\""
        self["structType"] = "struct"
        self["doxyString"] = ""
        self["baseClasses"] = []
        self["baseClassesInit"] = []
        self["simpleMembers"] = []
        self["complexMembers"] = []
        self["author"] = "MacJariel"
        self["flaggedMembers"] = False
        self.__implicitList = None

    def setImplicitList(self, scalarType, complexType = True, itemsName = None, useSharedPointer = False):
        assert self.__implicitList == None
        assert len(self["complexMembers"]) == 0
        self.__implicitListSharedPtr = useSharedPointer
        if self.__implicitListSharedPtr:
            self.__implicitListOriginalType = scalarType
            scalarType = scalarType + "Ptr"
        self.__implicitList = scalarType
        self.__implicitListComplexType = complexType
        self.__implicitListItemsName = itemsName
        self["baseClasses"].append("public QList<%s>" % (scalarType))

    def genFwdDecl(self):
        return "%(structType)s %(structName)s;" % self.data

    def genShrPtrDecl(self):
        return "typedef QSharedPointer<%(structName)s> %(structName)sPtr;" % self.data

    def genDef(self):
        doxyString = genDoxyString(self["doxyString"], self["author"])
        baseClasses = ", ".join(self["baseClasses"])
        if (len(baseClasses) > 0):
            baseClasses = ": " + baseClasses
        baseClassesInit = ", ".join(self["baseClassesInit"])
        resLines = [""]
        resLines.append(doxyString)
        resLines.append("struct %s%s {" % (self["structName"], baseClasses))
        members = list(self["simpleMembers"])
        members.extend(self["complexMembers"])

        if self["flaggedMembers"]:
            resLines.append("    enum {")
            attrs = ["        NoAttr = 0"]
            attrValue = 1
            for m in members:
                if (m["flagged"]):
                    attrName = m["flagName"]
                    attrs.append("        %s = %d" % (attrName, attrValue))
                    attrValue *= 2
            resLines.append(",\n".join(attrs))
            resLines.append("    };")
        inits = self.inits()
        if (len(inits) > 0):
            resLines.extend(constrWrapper.wrap("    %s(): %s {}\n" % (self["structName"], ", ".join(inits))))


        for m in members:
            resLines.append("    %(type)s %(name)s; ///%(doxyString)s" % m)
        if self["flaggedMembers"]:
            resLines.append("\n    bool hasFlag(uint flag) const { return (m_flags & flag) != 0; }")
            resLines.append("    void setFlag(uint flag) { m_flags |= flag; }\n")
            resLines.append("private:\n    uint m_flags;")

        resLines.append("};\n")
        return "\n".join(resLines)

    def genWriter(self, writer, data, isPtr, elemName = None):
        res = []
        res.append("{")
        writeStartElement = writer + "->writeStartElement(%s);"
        writeEndElement   = writer + "->writeEndElement();"
        writeTextElement  = writer + "->writeTextElement(%s, toString(%s));"
        writeAttribute    = writer + "->writeAttribute(%s, toString(%s));"
        writeStruct       = "GameStructParser::write(" + writer + ", %s);"
        writeStruct2      = "GameStructParser::write(" + writer + ", %s, %s);"
        if isPtr:
            dataThis = "(*%s)" % data
            dataMbr = data + "->"
        else:
            dataMbr = data + "."
            dataThis = data

        if elemName:
            res.append("    " +  writeStartElement % "%s ? %s : %s" % (elemName, elemName, self["elemName"]))
        else:
            res.append("    " + writeStartElement % self["elemName"])

        for s in self["simpleMembers"]:
            writeString = writeAttribute % (s["elemName"], dataMbr + s["name"])
            if self["flaggedMembers"] and s["flagged"]:
                res.append("    if (%shasFlag(%s::%s)) {" % (dataMbr, self["structName"], s["flagName"]))
                res.append("         " + writeString)
                res.append("    }")
            else:
                res.append("    " + writeString)

        if (self.__implicitList):
            assert len(self["complexMembers"]) == 0
            res.append("    foreach (const %s& item, %s) {" % (self.__implicitList, dataThis))
            if (self.__implicitListComplexType):
                item = "item"
                if (self.__implicitListSharedPtr):
                    item = "*" + item
                res.append("        " + writeStruct % item)
            else:
                res.append("        " + writeTextElement % ("\"" + self.__implicitListItemsName + "\"", "item"))
            res.append("    }")

        for s in self["complexMembers"]:
            writeString = writeStruct2 % (dataMbr + s["name"], s["elemName"])
            if self["flaggedMembers"] and s["flagged"]:
                res.append("    if (%shasFlag(%s::%s)) {" % (dataMbr, self["structName"], s["flagName"]))
                res.append("         " + writeString)
                res.append("    }")
            else:
                res.append("    " + writeString)
        res.append("    " + writeEndElement)
        res.append("}\n")
        return "\n".join(res)

    def genReader(self, xmlNode, data = None, isPtr = True):
        res = []
        res.append("{")
        if data:
            dataVar = data
        else:
            dataVar = "res"
            if isPtr:
                res.append("    %(structName)sPtr res(new %(structName)s());" % self)
            else:
                res.append("    %(structName)s res;" % self)

        if isPtr:
            dataRef = "(*%s)" % dataVar
            dataMbr = dataVar + "->"
        else:
            dataRef = dataVar
            dataMbr = dataVar + "."

        for s in self["simpleMembers"]:
            readString = ("fromString(%(xmlNode)s->attribute(%(elemName)s), %(dataMbr)s%(name)s);" %
                       {"elemName": s["elemName"],
                        "name": s["name"],
                        "xmlNode": xmlNode,
                        "dataMbr": dataMbr})
            if self["flaggedMembers"] and s["flagged"]:
                res.append("    if (%s->hasAttribute(%s)) {" % (xmlNode, s["elemName"]))
                res.append("        %ssetFlag(%s::%s);" % (dataMbr, self["structName"], s["flagName"]))
                res.append("        " + readString)
                res.append("    }")
            else:
                res.append("    " + readString)

        if (self.__implicitList):
            assert len(self["complexMembers"]) == 0
            res.append("    %sclear();" % dataMbr)
            res.append("    foreach (XmlNode* ch, %s->getChildren()) {" % xmlNode)
            if self.__implicitListSharedPtr:
                res.append("        %s item(new %s);" % (self.__implicitList, self.__implicitListOriginalType));
            else:
                res.append("        %s item;" % self.__implicitList);

            if (self.__implicitListComplexType):
                if self.__implicitListSharedPtr:
                    res.append("        GameStructParser::read(ch, *item);")
                else:
                    res.append("        GameStructParser::read(ch, item);")
            else:
                res.append("        fromString(ch->text(), item);");
            res.append("        %sappend(item);" % dataMbr)
            res.append("    }")

        if (len(self["complexMembers"]) > 0):
            res.append("    foreach (XmlNode* ch, %s->getChildren()) {" % xmlNode)
            for s in self["complexMembers"]:
                res.append("        if (ch->name() == %(elemName)s) {" % s)
                if self["flaggedMembers"] and s["flagged"]:
                    res.append("        %ssetFlag(%s::%s);" % (dataMbr, self["structName"], s["flagName"]))
                res.append("            ")
                res.append("            GameStructParser::read(ch, %s%s);" % (dataMbr, s["name"]))
                res.append("            continue;")
                res.append("        }")
            res.append("    }")
        if (not data):
            res.append("    return %s;" % dataVar)
        res.append("}")
        return "\n".join(res)


    def inits(self):
        res = list(self["baseClassesInit"])
        for member in self["simpleMembers"]:
            if (member["initVal"]):
                res.append("%(name)s(%(initVal)s)" % member)
        if self["flaggedMembers"]:
            res.append("m_flags(NoAttr)")
        return res


class Action(Struct):
    def __init__(self, name, elemName):
        Struct.__init__(self, "Action" + name + "Data", elemName)
        self["actionName"] = name
        self["actionType"] = name + "Type"
        self["baseClasses"].append("public ActionData")
        self["baseClassesInit"].append("ActionData(" + self["actionType"] + ")")

class GameStruct(Struct):
    def __init__(self, name, elemName):
        Struct.__init__(self, name + "Data", elemName)
        self["gameStructName"] = name
        self["gameStructType"] = name + "DataType"
        self["baseClasses"].append("public GameStruct")
        self["baseClassesInit"].append("GameStruct(" + self["gameStructType"] + ")")

class ServerEvent(Struct):
    def __init__(self, name, elemName):
        Struct.__init__(self, name + "EventData", elemName)
        self["serverEventName"] = name
        self["serverEventType"] = name + "Type"
        self["baseClasses"].append("public ServerEventData")
        self["baseClassesInit"].append("ServerEventData(" + self["serverEventType"] + ")")

class GameEventCmd(Struct):
    def __init__(self, name, elemName):
        Struct.__init__(self, name + "CmdData", elemName)
        self["gameEventCmdName"] = name
        self["gameEventCmdType"] = name + "Type"
        self["baseClasses"].append("public GameEventCmdData")
        self["baseClassesInit"].append("GameEventCmdData(" + self["gameEventCmdType"] + ")")